package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.cart.pojo.Cart;
import com.atguigu.gmall.common.bean.ResponseVo;
import com.atguigu.gmall.common.exception.OrderException;
import com.atguigu.gmall.order.feign.*;
import com.atguigu.gmall.order.interceptor.LoginInterceptor;
import com.atguigu.gmall.order.pojo.UserInfo;
import com.atguigu.gmall.oms.vo.OrderConfirmVo;
import com.atguigu.gmall.oms.vo.OrderItemVo;
import com.atguigu.gmall.order.service.OrderService;
import com.atguigu.gmall.oms.vo.OrderSubmitVo;
import com.atguigu.gmall.pms.entity.SkuAttrValueEntity;
import com.atguigu.gmall.pms.entity.SkuEntity;
import com.atguigu.gmall.sms.vo.ItemSaleVo;
import com.atguigu.gmall.ums.entity.UserAddressEntity;
import com.atguigu.gmall.ums.entity.UserEntity;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.atguigu.gmall.wms.vo.SkuLockVo;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private GmallPmsClient pmsClient;

    @Autowired
    private GmallSmsClient smsClient;

    @Autowired
    private GmallWmsClient wmsClient;

    @Autowired
    private GmallUmsClient umsClient;

    @Autowired
    private GmallCartClient cartClient;

    @Autowired
    private GmallOmsClient omsClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ExecutorService executorService;

    private static String KEY_PREFIX = "order:token:";

    public static void main(String[] args) {
    }
    @Override
    public OrderConfirmVo confirm() {
        OrderConfirmVo confirmVo = new OrderConfirmVo();

        //从拦截器获取登录用户的信息
        UserInfo userInfo = LoginInterceptor.getUserInfo();
        Long userId = userInfo.getUserId();
        // 1.根据userId查询用户的收货地址列表 v

        CompletableFuture<Void> userAddressCompletableFuture = CompletableFuture.runAsync(() -> {
            ResponseVo<List<UserAddressEntity>> userAddressResponseVo = umsClient.queryAddressesByUserId(userId);
            List<UserAddressEntity> userAddressEntities = userAddressResponseVo.getData();
            confirmVo.setAddresses(userAddressEntities);
        }, executorService);


        // 2.根据userId查询查询已选中的购物车记录 v
        CompletableFuture<List<Cart>> cartsCompletableFuture = CompletableFuture.supplyAsync(() -> {
            ResponseVo<List<Cart>> CartsResponseVo = cartClient.queryCheckedCartByUserId(userId);
            List<Cart> carts = CartsResponseVo.getData();
            if (CollectionUtils.isEmpty(carts)) {
                throw new OrderException("请添加商品到购物车!");
            }
            return carts;
        }, executorService);

        //遍历购物车集合 将购物车对象转换为 OrderItemVo
        CompletableFuture<Void> orderItemsCompletableFuture = cartsCompletableFuture.thenAcceptAsync(carts -> {
            List<OrderItemVo> items = carts.stream().map(cart -> {
                OrderItemVo orderItemVo = new OrderItemVo();
                //只能设置skuId 和count属性 其他属性实时查询
                Long skuId = cart.getSkuId();
                orderItemVo.setSkuId(skuId);
                orderItemVo.setCount(cart.getCount());

                // 3.根据skuId查询sku v
                CompletableFuture<Void> skuCompletableFuture = CompletableFuture.runAsync(() -> {
                    ResponseVo<SkuEntity> skuEntityResponseVo = pmsClient.querySkuById(skuId);
                    SkuEntity skuEntity = skuEntityResponseVo.getData();
                    orderItemVo.setDefaultImage(skuEntity.getDefaultImage());
                    orderItemVo.setPrice(skuEntity.getPrice());
                    orderItemVo.setTitle(skuEntity.getTitle());
                    orderItemVo.setWeight(skuEntity.getWeight());
                }, executorService);

                // 4.根据skuId查询销售属性 v
                CompletableFuture<Void> skuattrCompletableFuture = CompletableFuture.runAsync(() -> {
                    ResponseVo<List<SkuAttrValueEntity>> skuAttrsResponseVo = pmsClient.querySkuAttrsBySkuId(skuId);
                    List<SkuAttrValueEntity> skuAttrValueEntities = skuAttrsResponseVo.getData();
                    orderItemVo.setSaleAttrs(skuAttrValueEntities);
                }, executorService);

                // 5.根据skuId查询库存 v
                CompletableFuture<Void> wareskuCompletableFuture = CompletableFuture.runAsync(() -> {
                    ResponseVo<List<WareSkuEntity>> wareSkuResponseVo = wmsClient.queryWareSkusBySkuId(skuId);
                    List<WareSkuEntity> wareSkuEntities = wareSkuResponseVo.getData();
                    orderItemVo.setStore(wareSkuEntities.stream().anyMatch(wareSkuEntity -> wareSkuEntity.getStock() - wareSkuEntity.getStockLocked() > 0));
                }, executorService);
                // 6.根据skuId查询营销信息 v
                CompletableFuture<Void> salesCompletableFuture = CompletableFuture.runAsync(() -> {
                    ResponseVo<List<ItemSaleVo>> saleResponseVo = smsClient.queryItemSalesBySkuId(skuId);
                    List<ItemSaleVo> itemSaleVos = saleResponseVo.getData();
                    orderItemVo.setSales(itemSaleVos);
                }, executorService);

                CompletableFuture.allOf(salesCompletableFuture, wareskuCompletableFuture, skuattrCompletableFuture, skuCompletableFuture).join();
                return orderItemVo;
            }).collect(Collectors.toList());
            confirmVo.setItems(items);
        }, executorService);

        // 7.根据userId查询用户 v
        CompletableFuture<Void> userCompletableFuture = CompletableFuture.runAsync(() -> {
            ResponseVo<UserEntity> userEntityResponseVo = umsClient.queryUserById(userId);
            UserEntity userEntity = userEntityResponseVo.getData();
            if (userEntity != null) {
                confirmVo.setBounds(userEntity.getIntegration());
            }
        }, executorService);

        // 生成orderToken 页面 redis 雪花算法 订单号
        CompletableFuture<Void> tokenCompletableFuture = CompletableFuture.runAsync(() -> {
            String orderToken = IdWorker.getIdStr();
            confirmVo.setOrderToken(orderToken);
            redisTemplate.opsForValue().set(KEY_PREFIX + orderToken, orderToken);
        }, executorService);

        CompletableFuture.allOf(tokenCompletableFuture,userCompletableFuture,orderItemsCompletableFuture,userAddressCompletableFuture).join();

        return confirmVo;
    }

    @Override
    public void submit(OrderSubmitVo submitVo) {
        // 1.防重：根据页面的orderToken到redis中查询，如果存在则放行，否则直接抛出异常
        String orderToken = submitVo.getOrderToken(); //页面中的token
        if (StringUtils.isBlank(orderToken)){
            //抛出异常
            throw new OrderException("非法请求!");
        }

        //获取redis中的orderToken 判断和页面中的是否一致 一致表示正常请求 然后删除redis中的orderToken 防止重复提交
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                "then " +
                "   return redis.call('del', KEYS[1]) " +
                "else " +
                "   return 0 " +
                "end";
        Boolean flag = this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(KEY_PREFIX + orderToken), orderToken);
        if (!flag){
            throw new OrderException("请不要重复提交！");
        }

        // 2.验总价：页面中提交的总价格 和 数据库中实时总价格 比较，如果不一致直接抛出异常
        // 判断送货清单是否合法
        List<OrderItemVo> items = submitVo.getItems();
        if (CollectionUtils.isEmpty(items)){
            throw new OrderException("请选择需要购物的商品!");
        }

        // 获取页面提交的总价格
        BigDecimal totalPrice = submitVo.getTotalPrice();

        //数据库查询总价格
        BigDecimal curTotalPrice = items.stream().map(item -> {
            //根据item中是skuId查询实时价格
            ResponseVo<SkuEntity> skuEntityResponseVo = pmsClient.querySkuById(item.getSkuId());
            SkuEntity skuEntity = skuEntityResponseVo.getData();
            if (skuEntity == null) {
                return new BigDecimal(0);
            }
            return skuEntity.getPrice().multiply(item.getCount()); //实时小计
        }).reduce((a, b) -> a.add(b)).get();

        //比较价格 不相等 页面已过期
        if (curTotalPrice.compareTo(totalPrice) !=0){
            throw new OrderException("页面以过期!");
        }

        // TODO: 限购件数
        // 3.验库存并锁定库存（保证原子性）
        //将items集合转换为skuLockV集合
        List<SkuLockVo> skuLocks = items.stream().map(item -> {
            SkuLockVo skuLockVo = new SkuLockVo();
            skuLockVo.setSkuId(item.getSkuId());
            skuLockVo.setCount(item.getCount().intValue());
            return skuLockVo;
        }).collect(Collectors.toList());

        //调用远程接口锁定库存
        ResponseVo<List<SkuLockVo>> lockResponseVo = wmsClient.checkAndLock(skuLocks, orderToken);
        List<SkuLockVo> skuLockVos = lockResponseVo.getData();

        //判断 不为空表示锁定失败
        if (!CollectionUtils.isEmpty(skuLockVos)){
            throw new OrderException(JSON.toJSONString(skuLockVos));
        }

//        int i = 1/0;
        // 4.创建订单 调用oms远程接口 创建订单失败 需要解锁库存
        //获取userId
        UserInfo userInfo = LoginInterceptor.getUserInfo();
        Long userId = userInfo.getUserId();
        try {
            omsClient.saveOrder(submitVo,userId);
            //创建订单文完成后 发送延时消息给mq 完成定时关单 发送订单编号
            rabbitTemplate.convertAndSend("ORDER.EXCHANGE","order.ttl",orderToken);
        } catch (Exception e) {
            e.printStackTrace();
            //如果创建订单失败 发送消息给wms 立刻解锁库存 标记为无效订单
            rabbitTemplate.convertAndSend("ORDER.EXCHANGE","order.fail",orderToken);
            throw new OrderException("服务器内部错误!");

        }
        // 5.异步删除购物车中对应的记录（MQ）
        //发送消息给cart 删除对应的购物车
        Map<String,Object> msg = new HashMap<>();
        msg.put("userId",userId);
        msg.put("skuIds",JSON.toJSONString(items.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList())));
        rabbitTemplate.convertAndSend("ORDER.EXCHANGE","cart.delete",msg);
    }
}
